package com.yzd.learnjava.concurrency;

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;

import com.yzd.learnjava.util.Print;

public class PriorityBlockingQueueDemo {
	public static void main(String[] args) {
		Random random = new Random(47);
		ExecutorService exec = Executors.newCachedThreadPool();
		PriorityBlockingQueue<Runnable> queue = new PriorityBlockingQueue<Runnable>();
		exec.execute(new PrioritizedTaskProducer(queue, exec));
		exec.execute(new PrioritizedTaskConsumer(queue));
	}
}

class PrioritizedTask implements Runnable , Comparable<PrioritizedTask>{
	private Random random = new Random(47);
	private static int counter = 0;
	private final int id = counter++;
	private final int priority ;
	protected static List<PrioritizedTask> sequence = new ArrayList<PrioritizedTask>();
	public PrioritizedTask(int priority) {
		this.priority = priority;
		sequence.add(this);
	}
	
	public int compareTo(PrioritizedTask task) {
		return priority < task.priority ? 1 : (priority > task.priority ? -1 : 0);
	}
	
	public void run() {
		try {
			TimeUnit.MILLISECONDS.sleep(random.nextInt(250));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public String toString() {
		return String.format("[%1$-3d]", priority) + " Task " + id;
	}
	
	public String summary() {
		return "(" + id + ":" + priority + ")";
	}
	
	public static class EndSentinel extends PrioritizedTask{
		private ExecutorService exec ;
		public EndSentinel(ExecutorService e) {
			super(-1);
			exec = e ;
		}
		public void run() {
			int count = 0;
			for(PrioritizedTask task : PrioritizedTask.sequence) {
				Print.printnb(task.summary());
				if((++count % 5 == 0)) {
					Print.print();
				}
			}
			Print.print();
			Print.print(this + " Calling shutdownNow()");
			exec.shutdownNow();
		}
	}
}

class PrioritizedTaskProducer implements Runnable{
	private Random random = new Random(47);
	private Queue<Runnable> queue ;
	private ExecutorService exec ;
	
	public PrioritizedTaskProducer(Queue<Runnable> queue , ExecutorService e) {
		this.queue = queue;
		exec = e;
	}
	
	public void run() {
		for(int i = 0 ; i < 20; i++) {
			queue.add(new PrioritizedTask(random.nextInt(10)));
			Thread.yield();
		}
		
		try {
			for(int i = 0 ; i < 10; i++) {
				TimeUnit.MILLISECONDS.sleep(250);
				queue.add(new PrioritizedTask(10));
			}
			
			for(int i = 0 ; i < 10; i++) {
				queue.add(new PrioritizedTask(i));
			}
			
			queue.add(new PrioritizedTask.EndSentinel(exec));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		Print.print("Finished PrioritizedTaskProducer");
	}
}

class PrioritizedTaskConsumer implements Runnable{
	private PriorityBlockingQueue<Runnable> q ;
	public PrioritizedTaskConsumer(PriorityBlockingQueue<Runnable> q) {
		this.q = q ;
	}
	
	public void run() {
		try {
			while(!Thread.interrupted()) {
				q.take().run();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		Print.print("Finished PrioritizedTaskConsumer");
	}
}





















